<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 异常处理 没法保证它百分之百正确 try catch
        // bug---区分
        // 如果执行的语句中有一句产生错误，后面的语句不会继续执行
        /**console.log(aaa);
        console.log(456);*/
        // 捕获异常 后面继续运行
        // catch 块指定一个标识符（在上面的示例中为 e），该标识符保存抛出异常信息
        try {
            console.log(aaa);
        } catch (e) {
            console.log("报错信息：" + e); //报错信息：ReferenceError: aaa is not defined
        }
        console.log(456); //456

        // catch 才能捕获到异常信息;finally是一定会执行的; try必选
        try {
            console.log(aaa);
        } finally {
            console.log(123);//始终会执行的代码
        }
        console.log(456);
        // 报错只输出123 不输出456

        // 标识符仅在 catch 块执行时存在；catch 块执行完成后，标识符不再可用
        try {
            console.log(aaa);
        } catch (e) {
            console.log(typeof e);
            console.dir(e);
            console.log("报错信息：" + e);
        } finally {
            console.log(e);//此处会报错，因为e在catch中可以访问，在此处无法访问到
        }
        console.log(456);

        try {
            console.log(aaa);
        } catch (e) {
            console.log("e.name: " + e.name);
            console.log("e.message: " + e.message);
            console.log("e.stack: " + e.stack);
            //浏览器还对error对象的属性做了扩展，添加了其他相关信息其中各浏览器厂商实现最多的是stack属性，它表示栈跟踪信息(safari不支持)  stack是实例的自有属性，不是继承属性
            console.log(e.hasOwnProperty("stack"));//true  
            console.log("报错信息：" + e);
            // console.log(e.constructor.name);
        }
        // e.name: ReferenceError
        // e.message: aaa is not defined
        // e.stack: ReferenceError: aaa is not defined
        // true
        // 报错信息：ReferenceError: aaa is not defined
        function add(){
            try {
                console.log(aaa);
            } catch (e) {
                console.log("e.name: " + e.name);
                console.log("e.message: " + e.message);
                console.log("e.stack: " + e.stack);
                //浏览器还对error对象的属性做了扩展，添加了其他相关信息其中各浏览器厂商实现最多的是stack属性，它表示栈跟踪信息(safari不支持)  stack是实例的自有属性，不是继承属性
                console.log(e.hasOwnProperty("stack"));//true  
                console.log("报错信息：" + e);
                // console.log(e.constructor.name);
            }
            // e.name: ReferenceError
            // e.message: aaa is not defined
            // e.stack: e.stack: ReferenceError: aaa is not defined at add at anonymous
            // true
            // 报错信息：ReferenceError: aaa is not defined
        }
        add()

        //throw 抛出自定义错误
        try {
            throw "主动抛出错误";
        } catch (e) {
            console.log("报错信息：" + e); //报错信息：主动抛出错误
        } finally {
            console.log(123); //123
        }
        console.log(456); //456


        // 例题 可以提高程序健壮性
        function getRectArea(width, height) {
            if (isNaN(width) || isNaN(height)) {
                throw "Parameter is not a number!";
            }
        }

        try {
            getRectArea(3, 'A');
        } catch (e) {
            console.log(e); // "Parameter is not a number!"
        }

        //例3
        try {
            // throw 49; // 抛出一个数值异常----异常在 1-50 之间时，直接处理
            throw 51; // 抛出一个数值异常----报错 因为没有进行异常捕获---Uncaught 51
        } catch (e) {
            if (e <= 50) {
                // 异常在 1-50 之间时，直接处理
                console.log("异常在 1-50 之间时，直接处理");
            } else {
                // 异常无法处理，重新抛出
                throw e;
            }
        }
        
        try{
            try{
                throw "oops";
            }catch(ex){
                console.error("inner:",ex);
            }finally{
                console.log("finally");
            }
        }catch(ex){
            console.error("outer:",ex);
        }
        // 报错inner: oops
        // finally

        try{
            try{
                throw "oops";
            }catch(ex){
                console.log("inner:",ex);
            }finally{
                console.log("finally");
            }
        }catch(ex){
            console.log("outer:",ex);
        }
        // inner: oops
        // finally

        //1
        try {
            try {
                throw "oops";
            } finally {
                console.log("finally");
            }
        } catch (ex) {
            console.error("outer", ex);
        }
        // Output:
        // "finally"
        // 报错"outer" "oops"

        try {
            try {
                throw "oops";
            } catch (ex) {
                console.error("inner", ex);
                throw ex;
            } finally {
                console.log("finally");
            }
        } catch (ex) {
            console.error("outer", ex);
        }
        // Output:
        // 报错"inner" "oops"
        // "finally"
        // 报错"outer" "oops"


        //如果try的catch-block中发生另一个异常，则包裹该try..catch的外部try-block中的剩余代码将不会执行，因为控制流会立即跳转到外部try的catch-block。
        try {
            try {
                throw "ErrorMessage11";
            } catch (e) {
                throw "ErrorMessage22"; //抛出异常后将直接跳出catch，catch内后续代码不再执行
                console.log("inside catch", e);
                throw "ErrorMessage33";
            } finally {
                console.log("finally 111");
            }
        } catch (e) {
            console.log("outside catch", e);
        } finally {
            console.log("finally 222");
        }
        // finally 111
        // outside catch ErrorMessage22
        // finally 222

        try {
            try {
                throw "ErrorMessage11";
            } catch (e) {
                // throw "ErrorMessage22"; //抛出异常后将直接跳出catch，catch内后续代码不再执行
                console.log("inside catch", e);
                // throw "ErrorMessage33";
            } finally {
                console.log("finally 111");
            }
        } catch (e) {
            console.log("outside catch", e);
        } finally {
            console.log("finally 222");
        }
        // inside catch ErrorMessage11
        // finally 111
        // finally 222

        try {
            try {
                throw "ErrorMessage11";
            } catch (e) {
                // throw "ErrorMessage22"; //抛出异常后将直接跳出catch，catch内后续代码不再执行
                console.log("inside catch", e);
                throw "ErrorMessage33";
            } finally {
                console.log("finally 111");
            }
        } catch (e) {
            console.log("outside catch", e);
        } finally {
            console.log("finally 222");
        }
        // inside catch ErrorMessage11
        // finally 111
        // outside catch ErrorMessage33
        // finally 222

        try {
            function abc(x, cb) {
                console.log(x);
                cb();
            }
            abc("xx", function () {
                // var arr = new Array(-1); //有弹窗
                // var arr = new Array(5); //无弹窗
            });
        } catch (e) {
            // console.log(e);
            switch (e.constructor.name) {
                case "RangeError":
                    alert("请输入正确范围的数!")
                    break;
                default:
                    break;
            }
        }
        
        //JS的异步
        //思考这种情况是否能捕获到异常，回调函数捕获异常的问题
        try {
            function abc(x, cb) {
                console.log(x);
                cb();
            }
        } catch (e) {
            console.log(e);
        }
        abc("xx", function () {
            var arr = new Array(-1); //范围错误
        });
        // 捕获不到 作用域 只能写到try catch里面
        
    </script>
</body>
</html>